package com.sika.code.infractructure.db.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.sika.code.infractructure.db.properties.CustomerShardingProperties;
import com.sika.code.infractructure.db.properties.ShardingItem;
import com.sika.code.infractructure.db.util.CustomerStandardShardingStrategyConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.NoneShardingStrategyConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.ShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;

/**
 * <p>
 * 使用sharingjdbc基础设置
 * </p>
 *
 * @author daiqi
 * @date 2018/12/4 9:38
 */
public abstract class ShardingDataSourceConfig {

    protected void buildDefaultDataSourceConfig(ShardingRuleConfiguration shardingRuleConfig) {
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new NoneShardingStrategyConfiguration());
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new NoneShardingStrategyConfiguration());
        shardingRuleConfig.setDefaultDataSourceName(getCustomerShardingProperties().getDefaultShardingItem().getDataSourceName());
    }

    public void buildSharingTableRuleConfiguration(ShardingRuleConfiguration shardingRuleConfig) {
        Map<String, ShardingItem> shardingItemMap = getCustomerShardingProperties().getShardingItemMap();
        if (CollUtil.isEmpty(shardingItemMap)) {
            return;
        }
        Collection<TableRuleConfiguration> tableRuleConfigurations = shardingRuleConfig.getTableRuleConfigs();
        for (Map.Entry<String, ShardingItem> entry : shardingItemMap.entrySet()) {
            String logicTableName = entry.getKey();

            ShardingItem shardingItem = entry.getValue().build();
            String shardingColumn = StrUtil.join(StrUtil.COMMA, shardingItem.getShardingColumn(), shardingItem.getShardingPlaceholderColumn());
            ShardingStrategyConfiguration dataConfiguration = new CustomerStandardShardingStrategyConfiguration(shardingColumn, shardingItem.getDataSourceAlgorithmClassName()).build();
            ShardingStrategyConfiguration tableConfiguration = new CustomerStandardShardingStrategyConfiguration(shardingColumn, shardingItem.getTableAlgorithmClassName()).build();

            TableRuleConfiguration tableRuleConfiguration = new TableRuleConfiguration(logicTableName, shardingItem.getDataSourceName() + "." + logicTableName);
            tableRuleConfiguration.setDatabaseShardingStrategyConfig(dataConfiguration);
            tableRuleConfiguration.setTableShardingStrategyConfig(tableConfiguration);

            tableRuleConfigurations.add(tableRuleConfiguration);
        }
    }

    protected DataSource buildDataSource(Map<String, DataSource> dataSourceMap) {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        // 构建默认的数据源配置
        buildDefaultDataSourceConfig(shardingRuleConfig);
        // 构建分片规则
        buildSharingTableRuleConfiguration(shardingRuleConfig);
        // prop
        Properties prop = getCustomerShardingProperties().getProperties();
        try {
            return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, prop);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 需要手动配置事务管理器
     *
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    protected abstract CustomerShardingProperties getCustomerShardingProperties();
}
